﻿using System;
using System.Collections;
using System.Collections.Generic;
using DKE_Kinect.Entwicklung.Core;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace DKE_Kinect.Entwicklung.Grafik.ControlManaging
{
	/// <summary>
	/// Stellt eine einfache Komponente für das XNA - Window dar, welches lediglich den Manager für die Komponenten darstellt.
	/// </summary>
	public class Control : IContainer
	{
		#region Fields

		// Basiseigenschaften
		/// <summary>
		/// Gibt an, ob die Komponente aktiviert ist.
		/// </summary>
		protected bool pEnabled;
		/// <summary>
		/// Sichtbarkeit des Steuerelements.
		/// </summary>
		protected bool pVisible;
		/// <summary>
		/// Gibt den eindeutig identifizierenden Namen für die Komponente an.
		/// </summary>
		protected string pName;

		// Größen und Layout des Steuerelements
		/// <summary>
		/// Gibt das Layout an.
		/// </summary>
		protected Layout pLayout;

		/// <summary>
		/// Position und Größe.
		/// </summary>
		protected Point pLocation, pSize;
		/// <summary>
		/// Position und Größe bzw. Grenzen des Steuerelements.
		/// </summary>
		protected Rectangle pBounds;
		/// <summary>
		/// Gibt den relativen bzw. sichtbaren Teil des Steuerelements auf dem Bildschirm an.
		/// </summary>
		protected Rectangle? pRelativeScreenBounds, pVisibleScreenBounds;
		/// <summary>
		/// Gibt die Grenzen für die (wenn notwendig auszuschneidende) Textur an.
		/// </summary>
		protected Rectangle? pTextureBounds;

		// Hintergrund und Schrift:
		/// <summary>
		/// Alphawert für Schrift und Hintergrund.
		/// </summary>
		protected float pAlpha;
		/// <summary>
		/// Zwischenzeitlicher Alphawert für grafische Übergänge. Wenn dieser null ist, sollte der normale pAlpha - Wert genutzt werden. Ansonsten dieser.
		/// </summary>
		protected float? pBufferedAlpha;

		/// <summary>
		/// Gibt die Textur an, die gezeichnet werden soll.
		/// </summary>
		protected Texture2D pTexture;
		/// <summary>
		/// Gibt die Färbung für die Textur an.
		/// </summary>
		protected Color pTeint;
		/// <summary>
		/// Der darzustellende Text.
		/// </summary>
		protected string pText;
		/// <summary>
		/// Stellt die Schrift für den Text dar.
		/// </summary>
		protected SpriteFont pFont;
		/// <summary>
		/// Farbe der Schriftart.
		/// </summary>
		protected Color pFontColor;

		private TextureAlphaFadeTransition pFadeVisible;

		/// <summary>
		/// Gibt an, ob der Cursor auf dem Control ist.
		/// </summary>
		protected bool pMouseHovers;

		/// <summary>
		/// Gibt an, ob ein Update gefordert ist.
		/// </summary>
		protected bool pRefreshRequested;
		/// <summary>
		/// Gibt den aktuellen Parent dieser Komponente an.
		/// </summary>
		protected IContainer pParent;
		/// <summary>
		/// Gibt die Komponentensammlung an.
		/// </summary>
		protected ControlCollection pControls;

		// Readonlys / Constants
		private readonly Point startSize = new Point(150, 30);

		#endregion

		#region Constructors

		/// <summary>
		/// Instanziiert ein neues Objekt dieser Klasse.
		/// </summary>
		/// <param name="Name">Der eindeutig identifizierende Name des Steuerelements.</param>
		public Control (string Name)
		{
			if (Name == null)
				throw new NullReferenceException();
			if (Name.Equals(string.Empty))
				throw new ArgumentException();

			this.Initialize();
			this.pName = Name;
		}
		/// <summary>
		/// Instanziiert ein neues Objekt dieser Klasse.
		/// </summary>
		/// <param name="Name">Der eindeutig identifizierende Name des Steuerelements.</param>
		/// <param name="Texture">Die anzuzeigende Textur.</param>
		public Control (string Name, Texture2D Texture)
			: this(Name)
		{
			if (Name == null)
				throw new NullReferenceException();
			if (Name.Equals(string.Empty))
				throw new ArgumentException();

			this.Initialize();

			if (Texture != null)
			{
				this.pTexture = Texture;
				this.pLayout = Layout.AutoSize;
				this.Bounds = new Rectangle(0, 0, Texture.Width, Texture.Height);
			}
		}

		/// <summary>
		/// Intitialisiert die Eigenschaftswerte für dieses Objekt.
		/// </summary>
		protected virtual void Initialize ()
		{
			this.pRefreshRequested = true;
			this.pEnabled = true;
			this.pVisible = true;
			this.pLocation = Point.Zero;
			this.pSize = startSize;
			this.pBounds = new Rectangle(0, 0, startSize.X, startSize.Y);
			this.pMouseHovers = false;
			this.pRelativeScreenBounds = null;
			this.pVisibleScreenBounds = null;

			this.pAlpha = 1f;
			this.pTeint = Color.White;
			this.pTexture = null;
			this.pText = string.Empty;
			this.pFont = ContainerRessources.GetFont("MV Boli");
			this.pFontColor = Color.WhiteSmoke;

			this.pParent = null;
			this.pLayout = Layout.AutoSize;
			this.pControls = new ControlCollection(this);

			this.pFadeVisible = new TextureAlphaFadeTransition(0f, this.pAlpha, (new TimeSpan(0, 0, 0, 0, 300)).Ticks);
			this.pFadeVisible.TransitionFinished += new EventHandler(this.FadeVisibleFinished);
			this.pFadeVisible.Interpolation = TransitionInterpolation.Cubic;
		}

		#endregion

		#region Properties

		/// <summary>
		/// Gibt an, ob bei der Gameloop die Methode Update() dieser Komponente sowie aller untergeordneten Komponenten aufgerufen werden soll.
		/// </summary>
		public bool Enabled
		{
			get { return this.pEnabled; }
			set
			{
				if (this.pEnabled != value)
					this.OnEnabledChanging(EventArgs.Empty);
			}
		}
		/// <summary>
		/// Gibt die Sichtbarkeit des Steuerelements an oder legt diese fest.
		/// </summary>
		public bool Visible
		{
			get { return this.pVisible; }
			set
			{
				if (this.pVisible != value)
					this.OnVisibleChanging(EventArgs.Empty);
			}
		}
		/// <summary>
		/// Gibt den identifizierenden Namen für diese Komponente an oder legt diese fest.
		/// </summary>
		public string Name
		{
			get { return this.pName; }
			set
			{
				this.pName = value;
			}
		}

		/// <summary>
		/// Gibt den darzustellenden Text an oder legt diesen fest.
		/// </summary>
		public string Text { get { return this.pText; } set { this.pText = value; } }
		/// <summary>
		/// Gibt die Schriftartbeschreibung an oder legt diese fest.
		/// </summary>
		public SpriteFont Font { get { return this.pFont; } set { this.pFont = value; } }

		/// <summary>
		/// Gibt die Position des Steuerelements an oder legt diese fest.
		/// </summary>
		public Point Location
		{
			get { return this.pLocation; }
			set
			{
				if (!this.pLocation.Equals(value))
				{
					this.pLocation = value;
					this.pBounds.Location = value;
					this.OnLocationChanged(EventArgs.Empty);
				}
			}
		}
		/// <summary>
		/// Gibt die Größe des Steuerelements an oder legt diese fest.
		/// X entspricht der Breite.
		/// Y entspricht der Höhe.
		/// </summary>
		public Point Size
		{
			get { return this.pSize; }
			set
			{
				if (!this.pSize.Equals(value))
				{
					this.pSize = value;
					this.pBounds.Width = value.X;
					this.pBounds.Height = value.Y;
					this.OnSizeChanged(EventArgs.Empty);
				}
			}
		}
		/// <summary>
		/// Gibt die Grenzen des Steuerelements an oder legt diese fest.
		/// </summary>
		public Rectangle Bounds
		{
			get { return this.pBounds; }
			set
			{
				this.pBounds = value;
				this.Location = value.Location;
				this.Size = new Point(value.Width, value.Height);
			}
		}
		/// <summary>
		/// Gibt die relativen Grenzen gegenüber dem Bildschirm an.
		/// </summary>
		public Rectangle? RelativeScreenBounds { get { return this.pRelativeScreenBounds; } }
		/// <summary>
		/// Gibt den sichtbaren Teil des Controls an.
		/// </summary>
		public Rectangle? VisibleScreenBounds { get { return this.pVisibleScreenBounds; } }
		/// <summary>
		/// Gibt an, ob sich der Cursor auf einem Objekt befindet
		/// </summary>
		public bool MouseHovers { get { return this.pMouseHovers; } }

		/// <summary>
		/// Gibt den Alphawert zwischen 0 und 1 an oder legt diesen fest.
		/// 1 entspricht völliger Undurchsichtigkeit.
		/// 0 entspricht völliger Transparenz, sodass das Objekt nicht mehr sichtbar ist.
		/// </summary>
		/// <exception cref="ArgumentOutOfRangeException">Wird ausgelöst, wenn der setter nicht zwischen 0 und 1 liegt.</exception>
		public float Alpha
		{
			get { return this.pAlpha; }
			set
			{
				if ((value < 0) || (value > 1))
					throw new ArgumentOutOfRangeException();

				if (!this.pAlpha.Equals(value))
				{
					this.pAlpha = value;
					this.OnAlphaChanged(EventArgs.Empty);
				}
			}
		}
		/// <summary>
		/// Gibt die Textur für dieses Steuerelement an oder legt diese fest.
		/// Die Texture wird unskaliert in der oberen linken Ecke des Steuerelements angezeigt.
		/// </summary>
		public Texture2D Texture
		{
			get { return this.pTexture; }
			set
			{
				this.pTexture = value;
				this.UpdateTextureBounds();
			}
		}

		/// <summary>
		/// Gibt die Färbung der Textur an oder legt diese fest.
		/// </summary>
		public Color Teint { get { return this.pTeint; } set { this.pTeint = value; } }

		/// <summary>
		/// Gibt an, ob die Texture an die Größe des Steuerelements angepasst wird.
		/// </summary>
		public Layout Layout
		{
			get { return this.pLayout; }
			set
			{
				if (value != this.pLayout)
				{
					this.pLayout = value;
					this.UpdateTextureBounds();
				}
			}
		}

		/// <summary>
		/// Gibt die übergeordnete Komponente von dieser an oder legt diese fest.
		/// </summary>
		public IContainer Parent
		{
			get { return this.pParent; }
			set
			{
				if (value != this.pParent)
				{
					// Falls Komponente unabhängig wird.
					if (value == null)
						this.pParent.Controls.Remove(this);
					else
						value.Controls.Add(this);
				}
			}
		}
		/// <summary>
		/// Gibt die untergeordneten Komponenten von dieser zurück.
		/// </summary>
		public ControlCollection Controls { get { return this.pControls; } }

		#endregion

		#region Methods

		internal void setParent (IContainer Parent)
		{
			if (this.pParent != Parent)
			{
				this.pParent = Parent;
				this.OnParentChanged(EventArgs.Empty);
			}
		}

		/// <summary>
		/// Erzwingt, dass dieses sowie alle untergeordneten Elemente neu angeordnet werden.
		/// </summary>
		public void Refresh ()
		{
			this.pRefreshRequested = true;

			foreach (Control ctrl in this.pControls)
				ctrl.Refresh();
		}

		/// <summary>
		/// Ordnet die Steuerelemente horizontal an.
		/// </summary>
		/// <param name="Start">Bestimmt den oberen linken Punkt zum Start des anordnens.</param>
		/// <param name="GapSize">Bestimmt die Lücke zwischen den Steuerelementen.</param>
		public virtual void OrderControls (Point Start, int GapSize)
		{
			if (this.pControls.Count > 0)
			{
				this.pControls[0].Location = Start;

				for (int i = 1; i < this.pControls.Count; i++)
				{
					this.pControls[i].Location = new Point(Start.X, this.pControls[i - 1].Bounds.Bottom + GapSize);
				}
			}
		}

		/// <summary>
		/// Führt ein Update für diese und alle untergeordneten Komponenten aus.
		/// </summary>
		/// <param name="gameTime">Die Spielzeiteigenschaften.</param>
		public virtual void Update (GameTime gameTime)
		{
			if (this.pRefreshRequested)
			{
				this.UpdateScreenBounds();
				this.pRefreshRequested = false;
			}

			if (this.pVisibleScreenBounds.HasValue)
				this.pFadeVisible.Update(gameTime.ElapsedGameTime.Ticks);
			else if (this.pFadeVisible.Working)
				this.pFadeVisible.Finish();

			if (this.pFadeVisible.Working)
				this.pBufferedAlpha = this.pFadeVisible.Merged;
			else
				this.pBufferedAlpha = null;

			foreach (Control ctrl in this.pControls)
				if (ctrl.Enabled)
					ctrl.Update(gameTime);
		}

		/// <summary>
		/// Zeichnet das Steuerelement und alle untergeordneten Elemente, wenn die Eigenschaft Visible auf true gesetzt ist.
		/// Erst wird der Hintergrund gezeichnet.
		/// Danach werden die untergeordneten Steuerlemente gezeichnet.
		/// Zuletzt wird der Text ausgegeben, sofern vorhanden.
		/// </summary>
		/// <param name="gameTime">Die Spielzeiteigenschaften.</param>
		/// <param name="Sprite">Das Sprite, auf dem gezeichnet wird.</param>
		public virtual void Draw (GameTime gameTime, SpriteBatch Sprite)
		{
			if (this.pVisible)
			{
				// Hintergrund zeichnen:
				this.OnPaint(new PaintEventArgs(Sprite, gameTime));

				// Andere Steuerelemente zeichnen:
				foreach (Control ctrl in this.pControls)
					ctrl.Draw(gameTime, Sprite);

				// Text zeichnen:
				if ((this.pText != null) && (!this.pText.Equals(string.Empty)))
				{
					float alpha = (this.pFadeVisible.Working) ? this.pFadeVisible.Merged : this.pAlpha;
					Vector2 textSize = this.pFont.MeasureString(this.pText);
					if ((textSize.X <= this.pSize.X) && (textSize.Y <= this.pSize.Y) && (this.pRelativeScreenBounds.HasValue))
					{
						Vector2 drawTo = new Vector2(this.pRelativeScreenBounds.Value.Left + this.pBounds.Width / 2, this.pRelativeScreenBounds.Value.Top + this.pBounds.Height / 2);
						Sprite.DrawString(
							this.pFont,
							this.pText,
							drawTo,
							Color.WhiteSmoke * alpha,
							0f,
							textSize / 2,
							1f, SpriteEffects.None,
							0f);
					}
				}
			}
		}

		/// <summary>
		/// Passt die relativen und sichtbaren Grenzen für dieses Steuerelement ein.
		/// </summary>
		protected void UpdateScreenBounds ()
		{
			this.pRelativeScreenBounds = null;
			this.pVisibleScreenBounds = null;

			// Prüfen, ob das Control angezeigt werden kann:
			if ((this.pParent == null) || (!this.pParent.RelativeScreenBounds.HasValue))
				return;

			Rectangle thisBnds = this.pBounds;
			Rectangle parRel = this.pParent.RelativeScreenBounds.Value;

			// Relative Bildschirmgrenzen:
			Rectangle thisRel = new Rectangle(
				parRel.Left + thisBnds.Left,
				parRel.Top + thisBnds.Top,
				thisBnds.Width,
				thisBnds.Height);

			this.pRelativeScreenBounds = thisRel;

			// Sichtbare Bildschirmgrenzen:
			if ((this.pParent.VisibleScreenBounds.HasValue) && (this.pVisible))
			{
				Rectangle parVis = this.pParent.VisibleScreenBounds.Value;

				// Prüfen, ob die relative Position in den sichtbaren Grenzen des übergeordneten Steuerelements liegt:
				if ((thisRel.Left > parVis.Right) || (thisRel.Right <= parVis.Left) || (thisRel.Top > parVis.Bottom) || (thisRel.Bottom <= parVis.Top))
					return;

				Rectangle thisVis = new Rectangle();

				thisVis.X = Math.Max(thisRel.Left, parVis.Left);
				thisVis.Y = Math.Max(thisRel.Top, parVis.Top);

				// Breite einpassen:
				if (thisRel.Left < parVis.Left)
				{
					if (thisRel.Right >= parVis.Right)
						thisVis.Width = parVis.Width;
					else
						thisVis.Width = thisRel.Right - parVis.Left;
				}
				else
				{
					if (thisRel.Right >= parVis.Right)
						thisVis.Width = parVis.Right - thisRel.Left;
					else
						thisVis.Width = thisRel.Width;
				}

				// Höhe einpassen:
				if (thisRel.Top < parVis.Top)
				{
					if (thisRel.Bottom >= parVis.Bottom)
						thisVis.Height = parVis.Height;
					else
						thisVis.Height = thisRel.Bottom - parVis.Top;
				}
				else
				{
					if (thisRel.Bottom >= parVis.Bottom)
						thisVis.Height = parVis.Bottom - thisRel.Top;
					else
						thisVis.Height = thisRel.Height;
				}

				// Wert übergeben:
				this.pVisibleScreenBounds = thisVis;
			}

			this.UpdateTextureBounds();
		}
		/// <summary>
		/// Passt die Grenzen für die Textur ein.
		/// </summary>
		protected void UpdateTextureBounds ()
		{
			this.pTextureBounds = null;

			// Prüfen, ob Textur vorhanden ist bzw. ob das Steuerelement angezeigt wird:
			if ((this.pTexture != null) && (this.pVisibleScreenBounds.HasValue))
			{
				Rectangle rel = this.pRelativeScreenBounds.Value;
				Rectangle vis = this.pVisibleScreenBounds.Value;

				Rectangle tex = this.pTexture.Bounds;

				Rectangle src = new Rectangle();

				// Prüfen, ob die Texture auf die Größe des Steuerelements angepasst werden soll:
				switch (this.pLayout)
				{
					case Layout.None:
						// Breite einpassen:
						src.X = vis.Left - rel.Left;
						src.Width = vis.Width;

						if (src.Left >= tex.Width)
							return;

						src.Width = Math.Min(src.Width, tex.Width - src.X);

						// Höhe einpassen:
						src.Y = vis.Top - rel.Top;
						src.Height = vis.Height;

						if (src.Top >= tex.Height)
							return;

						src.Height = Math.Min(src.Height, tex.Height - src.Y);
						break;

					case Layout.Stretch:
						// Verhältnis des sichtbaren Teils des Steuerelements zur Textur berechnen:
						float leftRatio, rightRatio, topRatio, bottomRatio;
						leftRatio = (vis.Left - rel.Left) / (float)rel.Width;
						rightRatio = 1f - (rel.Right - vis.Right) / (float)rel.Width;
						topRatio = (vis.Top - rel.Top) / (float)rel.Height;
						bottomRatio = 1f - (rel.Bottom - vis.Bottom) / (float)rel.Height;

						src.X = (int)(leftRatio * tex.Width);
						src.Y = (int)(topRatio * tex.Height);
						src.Width = (int)((rightRatio - leftRatio) * tex.Width);
						src.Height = (int)((bottomRatio - topRatio) * tex.Height);
						break;

					case Layout.AutoSize:
						this.Size = new Point(tex.Width, tex.Height);
						goto case Layout.None;
				}

				// Wert übergeben:
				this.pTextureBounds = src;
			}
		}

		internal void PerformMouseClick ()
		{
			if (this.pEnabled)
				this.OnMouseClick(EventArgs.Empty);
		}
		internal void PerformMouseEnter ()
		{
			this.OnMouseEnter(EventArgs.Empty);
		}
		internal void PerformMouseLeave ()
		{
			this.OnMouseLeave(EventArgs.Empty);
		}
		internal void PerformMouseDown (Point Location)
		{
			if (this.pRelativeScreenBounds.HasValue)
			{
				this.OnMouseDown(new MouseEventArgs(new Point(
					Location.X - this.pRelativeScreenBounds.Value.Left,
					Location.Y - this.pRelativeScreenBounds.Value.Top)));
			}
		}
		internal void PerformMouseUp (Point Location)
		{
			if (this.pRelativeScreenBounds.HasValue)
			{
				this.OnMouseUp(new MouseEventArgs(new Point(
					Location.X - this.pRelativeScreenBounds.Value.Left,
					Location.Y - this.pRelativeScreenBounds.Value.Top)));
			}
		}
		internal void PerformMouseMove (Point Location)
		{
			if (this.pRelativeScreenBounds.HasValue)
			{
				this.OnMouseMove(new MouseEventArgs(new Point(
					Location.X - this.pRelativeScreenBounds.Value.Left,
					Location.Y - this.pRelativeScreenBounds.Value.Top)));
			}
		}

		// Event Handling
		/// <summary>
		/// Löst das Paint - Ereignis aus.
		/// </summary>
		/// <param name="e"></param>
		protected virtual void OnPaint (PaintEventArgs e)
		{
			if (this.pTextureBounds.HasValue)
			{
				float alpha = (this.pFadeVisible.Working) ? this.pFadeVisible.Merged : this.pAlpha;

				e.Sprite.Draw(
					this.pTexture,
					this.pVisibleScreenBounds.Value,
					this.pTextureBounds,
					this.pTeint * alpha);
			}

			if (this.Paint != null)
				this.Paint(this, e);
		}

		/// <summary>
		/// Löst das EnabledChanging - Ereignis aus. Beim überschreiben dieser Methode muss das Setzen der zugehörigen Eigenschaft Enabled selbst vorgenommen werden sowie das damit verbundende OnEnabledChanged - Ereignis.
		/// </summary>
		/// <param name="e"></param>
		protected virtual void OnEnabledChanging (EventArgs e)
		{
			if (this.EnabledChanging != null)
				this.EnabledChanging(this, e);

			this.pEnabled = !this.pEnabled;
			this.OnEnabledChanged(EventArgs.Empty);
		}
		/// <summary>
		/// Löst das EnabeldChanged - Ereignis aus.
		/// </summary>
		/// <param name="e"></param>
		protected virtual void OnEnabledChanged (EventArgs e)
		{
			if (this.pEnabled)
				this.Refresh();

			if (this.EnabledChanged != null)
				this.EnabledChanged(this, e);
		}

		private void FadeVisibleFinished (object sender, EventArgs e)
		{
			if (this.pFadeVisible.Reverted)
				this.onVisibleChanging(EventArgs.Empty);
		}
		/// <summary>
		/// Eigentliche OnVisibleChanging Methode.
		/// </summary>
		/// <param name="e"></param>
		protected void onVisibleChanging (EventArgs e)
		{
			if (this.VisibleChanging != null)
				this.VisibleChanging(this, e);

			this.pVisible = !this.pVisible;
			this.OnVisibleChanged(EventArgs.Empty);
		}
		/// <summary>
		/// Löst das VisibleChanging - Ereignis aus. Beim überschreiben dieser Methode muss ebenfalls das Setzen der zugehörigen Eigenschaft Visible selbst vorgenommen werden sowie das damit verbundende OnVisibleChanged - Ereignis.
		/// </summary>
		/// <param name="e"></param>
		protected virtual void OnVisibleChanging (EventArgs e)
		{
			if ((StateManager.CurrMode != eModus.mainmenu) && (StateManager.CurrMode != eModus.quickmenu))
				this.onVisibleChanging(e);
			else if (this.pFadeVisible.Working)
				this.pFadeVisible.Revert();
			else
			{
				if (this.pVisible)
					this.pFadeVisible.BeginFromBack();
				else
				{
					this.pFadeVisible.BeginFromFront();
					this.onVisibleChanging(e);
				}
			}
		}
		/// <summary>
		/// Löst das Ergebnis VisibleChanged aus.
		/// </summary>
		/// <param name="e"></param>
		protected virtual void OnVisibleChanged (EventArgs e)
		{
			this.Refresh();

			if (this.VisibleChanged != null)
				this.VisibleChanged(this, e);
		}
		/// <summary>
		/// Löst das ParentChanged - Ereignis aus.
		/// </summary>
		/// <param name="e"></param>
		protected virtual void OnParentChanged (EventArgs e)
		{
			this.Refresh();

			if (this.ParentChanged != null)
				this.ParentChanged(this, e);
		}
		/// <summary>
		/// Löst das Ereignis LocationChanged aus.
		/// </summary>
		/// <param name="e"></param>
		protected virtual void OnLocationChanged (EventArgs e)
		{
			this.Refresh();

			if (this.LocationChanged != null)
				this.LocationChanged(this, e);
		}
		/// <summary>
		/// Löst das Ereignis SizeChanged aus.
		/// </summary>
		/// <param name="e"></param>
		protected virtual void OnSizeChanged (EventArgs e)
		{
			this.Refresh();

			if ((this.pLayout == Layout.AutoSize) && (this.pTexture != null))
				this.pBounds = this.pTexture.Bounds;

			if (this.SizeChanged != null)
				this.SizeChanged(this, e);
		}

		/// <summary>
		/// Löst das Ereignis MouseClick aus.
		/// </summary>
		/// <param name="e"></param>
		protected virtual void OnMouseClick (EventArgs e)
		{
			if (this.MouseClick != null)
				this.MouseClick(this, e);
		}
		/// <summary>
		/// Löst das Ereignis MouseEnter aus.
		/// </summary>
		/// <param name="e"></param>
		protected virtual void OnMouseEnter (EventArgs e)
		{
			this.pMouseHovers = true;

			if (this.MouseEnter != null)
				this.MouseEnter(this, e);
		}
		/// <summary>
		/// Löst das Ereignis MouseLeave aus.
		/// </summary>
		/// <param name="e"></param>
		protected virtual void OnMouseLeave (EventArgs e)
		{
			this.pMouseHovers = false;

			if (this.MouseLeave != null)
				this.MouseLeave(this, e);
		}
		/// <summary>
		/// Löst das Ereignis MouseDown aus.
		/// </summary>
		/// <param name="e"></param>
		protected virtual void OnMouseDown (MouseEventArgs e)
		{
			if (this.MouseDown != null)
				this.MouseDown(this, e);
		}
		/// <summary>
		/// Löst das Ereignis MouseUp aus.
		/// </summary>
		/// <param name="e"></param>
		protected virtual void OnMouseUp (MouseEventArgs e)
		{
			if (this.MouseUp != null)
				this.MouseUp(this, e);
		}
		/// <summary>
		/// Löst das Ereignis MouseMove aus.
		/// </summary>
		/// <param name="e"></param>
		protected virtual void OnMouseMove (MouseEventArgs e)
		{
			if (this.MouseMove != null)
				this.MouseMove(this, e);
		}
		/// <summary>
		/// Tritt ein, wenn die Eigenschaft Alpha geändert wird.
		/// </summary>
		/// <param name="e"></param>
		protected virtual void OnAlphaChanged (EventArgs e)
		{ }

		// System.object Overrides
		/// <summary>
		/// Vergleicht zwei Komponenten mittels Gleichheit ihrer Namen.
		/// </summary>
		/// <param name="obj">Das vergleichende Objekt.</param>
		/// <returns></returns>
		public override bool Equals (object obj)
		{
			Control comp = obj as Control;
			return this.Equals(comp);
		}
		/// <summary>
		/// Vergleicht zwei Komponenten mittels Gleichheit ihrer Namen.
		/// </summary>
		/// <param name="obj">Das vergleichende Objekt.</param>
		/// <returns></returns>
		public bool Equals (Control obj)
		{
			if (obj == null)
				return false;

			return this.pName.Equals(obj.Name);
		}
		/// <summary>
		/// Gibt den Hashcode für diese Komponente zurück.
		/// </summary>
		/// <returns></returns>
		public override int GetHashCode ()
		{
			return this.pName.GetHashCode();
		}

		/// <summary>
		/// Gibt einen System.string zurück, der das akutelle Objekt darstellt.
		/// </summary>
		/// <returns></returns>
		public override string ToString ()
		{
			return this.pName + " as Control";
		}

		#endregion

		#region Events

		// Standard Events
		/// <summary>
		/// Tritt ein, bevor die Eigenschaft Enabled geändert wird.
		/// </summary>
		public event EventHandler EnabledChanging;
		/// <summary>
		/// Wird ausgelöst, wenn sich die Eigenschaft "Enabled" geändert hat.
		/// </summary>
		public event EventHandler EnabledChanged;

		/// <summary>
		/// Tritt ein, bevor die Eigenschaft Visible geändert wird.
		/// </summary>
		public event EventHandler VisibleChanging;
		/// <summary>
		/// Tritt ein, wenn die Eigenschaft Visible geändert wird.
		/// </summary>
		public event EventHandler VisibleChanged;

		/// <summary>
		/// Wird ausgelöst, wenn der Parent dieser Komponente geändert wurde.
		/// </summary>
		public event EventHandler ParentChanged;
		/// <summary>
		/// Tritt ein, wenn die Position des Steuerelements geändert wurde.
		/// </summary>
		public event EventHandler LocationChanged;
		/// <summary>
		/// Tritt ein, wenn die Größe des Steuerelements geändert wurde.
		/// </summary>
		public event EventHandler SizeChanged;

		// Mouse Events
		/// <summary>
		/// Tritt ein, wenn ein "Klick" auf das Steuerelement erfolgte.
		/// </summary>
		public event EventHandler MouseClick;
		/// <summary>
		/// Tritt ein, wenn die Maus beginnt, über dem Steuerelement zu sein.
		/// </summary>
		public event EventHandler MouseEnter;
		/// <summary>
		/// Tritt ein, wenn die Maus das Steuerelement verlässt.
		/// </summary>
		public event EventHandler MouseLeave;
		/// <summary>
		/// Tritt ein, wenn ein Mausbutton heruntergedrückt wurde.
		/// </summary>
		public event EventHandler<MouseEventArgs> MouseDown;
		/// <summary>
		/// Tritt ein, wenn ein Mausbutton losgelassen wurde.
		/// </summary>
		public event EventHandler<MouseEventArgs> MouseUp;
		/// <summary>
		/// Tritt ein, wenn eine Bewegung auf dem Steuerelement mit der Maus durchgeführt wurde.
		/// </summary>
		public event EventHandler<MouseEventArgs> MouseMove;

		// Background Painting
		/// <summary>
		/// Tritt ein, wenn der Hintergrund erneut gezeichnet wird.
		/// </summary>
		public event EventHandler<PaintEventArgs> Paint;

		#endregion
	}

	/// <summary>
	/// Stellt eine Auflistung von Komponenten in einem IContainer dar.
	/// </summary>
	public sealed class ControlCollection : IEnumerable
	{
		/// <summary>
		/// Der Eigentümer/Parent der Liste.
		/// </summary>
		private IContainer pOwner;
		/// <summary>
		/// Die Komponentenliste.
		/// </summary>
		private List<Control> pControls;


		/// <summary>
		/// Gets to array.
		/// </summary>
		public Control[] toArray
		{
			get { return this.pControls.ToArray(); }
		}
		/// <summary>
		/// Instanziiert ein neues Objekt dieser Klasse.
		/// </summary>
		/// <param name="Owner"></param>
		public ControlCollection (IContainer Owner)
		{
			if (Owner == null)
				throw new NullReferenceException();

			this.pOwner = Owner;
			this.pControls = new List<Control>();
		}

		/// <summary>
		/// Gibt die Anzahl an Komponenten im übergeordneten IContainer an.
		/// </summary>
		public int Count { get { return this.pControls.Count; } }

		/// <summary>
		/// Gibt eine Komponente aus der Auflistung zurück.
		/// </summary>
		/// <param name="Index">Der Index der Rückgabe - Komponente.</param>
		/// <returns></returns>
		public Control this[int Index]
		{
			get
			{
				if ((Index < 0) || (Index >= this.pControls.Count))
					throw new IndexOutOfRangeException();

				return this.pControls[Index];
			}
		}

		/// <summary>
		/// Fügt eine neue Komponnte der Auflistung hinzu, sofern diese nicht bereits exisitiert.
		/// Ist der Wert Comp.ChangingParent true, dann wird lediglich die Liste um die Komponente erweitert.
		/// </summary>
		/// <param name="Comp">Die hinzuzufügende Komponente.</param>
		public void Add (Control Comp)
		{
			if (Comp == null)
				throw new NullReferenceException();

			if (!this.pControls.Contains(Comp))
			{
				// Prüfen, ob das Steuerelement bereits einen anderen Parent besitzt:
				if (Comp.Parent != null)
				{
					Comp.Parent.Controls.Remove(Comp); // Entferne aus der anderen Liste das Control
					Comp.Parent.Refresh();
				}

				Comp.setParent(this.pOwner);
				this.pControls.Add(Comp);
				this.pOwner.Refresh();
			}
		}

		/// <summary>
		/// Entfernt eine Komponente aus der Auflistung.
		/// </summary>
		/// <param name="Index">Der Index, an dem die Komponente entfernt werden soll.</param>
		public void RemoveAt (int Index)
		{
			if ((Index < 0) && (Index >= this.pControls.Count))
				throw new IndexOutOfRangeException();

			this.pControls[Index].setParent(null);
			this.pControls.RemoveAt(Index);
			this.pOwner.Refresh();
		}

		/// <summary>
		/// Entfernt, sofern möglich, eine Komponente aus der Auflistung.
		/// </summary>
		/// <param name="Comp">Die zu entfernende Komponente.</param>
		public void Remove (Control Comp)
		{
			if (Comp == null)
				throw new NullReferenceException();

			this.pControls.Remove(Comp);
			Comp.setParent(null);
			this.pOwner.Refresh();
		}

		/// <summary>
		/// Gibt einen Enumerator zurück, der die Steuerelementeliste durchläuft.
		/// </summary>
		/// <returns></returns>
		public IEnumerator GetEnumerator ()
		{
			return this.pControls.GetEnumerator();
		}
		IEnumerator IEnumerable.GetEnumerator ()
		{
			return (IEnumerator)GetEnumerator();
		}
	}

	/// <summary>
	/// Repräsentiert Layoutmöglichkeiten für eine Texture in einem Steuerelement.
	/// </summary>
	public enum Layout
	{
		/// <summary>
		/// Die Textur wird unskaliert in die obere linke Ecke des Steuerelements platziert.
		/// </summary>
		None = 0,
		/// <summary>
		/// Die Textur wird auf die Größe des Steuerelements skaliert.
		/// </summary>
		Stretch = 1,
		/// <summary>
		/// Das Steuerelement wird auf die Größe der Textur angepasst.
		/// </summary>
		AutoSize = 2
	}

	/// <summary>
	/// Repräsentiert die Argumente beim Eintreten eines Paint - Events.
	/// </summary>
	public class PaintEventArgs : EventArgs
	{
		private SpriteBatch pSprite;
		private GameTime pTime;

		/// <summary>
		/// Instanziiert ein neues Objekt dieser Klase.
		/// </summary>
		/// <param name="Sprite"></param>
		/// <param name="Time"></param>
		public PaintEventArgs (SpriteBatch Sprite, GameTime Time)
		{
			this.pSprite = Sprite;
			this.pTime = Time;
		}

		/// <summary>
		/// Gibt das SpriteBatch zurück, dem das PaintEvent zugrunde liegt.
		/// </summary>
		public SpriteBatch Sprite { get { return this.pSprite; } }
		/// <summary>
		/// Die bis dahin verstrichene Zeit.
		/// </summary>
		public GameTime GameTime { get { return this.pTime; } }
	}
	/// <summary>
	/// Repräsentiert die Argumente beim Eintreten eines Mouse-Events
	/// </summary>
	public class MouseEventArgs : EventArgs
	{
		/// <summary>
		/// Position der Maus.
		/// </summary>
		protected Point pXY;

		/// <summary>
		/// Instianziiert ein neues Objekt dieser Klasse.
		/// </summary>
		/// <param name="XY">Position der Maus.</param>
		public MouseEventArgs (Point XY)
		{
			this.pXY = XY;
		}

		/// <summary>
		/// Gibt die Position des Cursors als Microsoft.Xna.Framework.Point an.
		/// </summary>
		public Point LocationPoint { get { return this.pXY; } }
		/// <summary>
		/// Gibt die Position des Cursors Microsoft.Xna.Framework.Vector2 an.
		/// </summary>
		public Vector2 LocationVector2 { get { return new Vector2(this.pXY.X, this.pXY.Y); } }

		/// <summary>
		/// Gibt die X - Position der Maus an.
		/// </summary>
		public int X { get { return this.pXY.X; } }
		/// <summary>
		/// Gibt die Y - Position der Maus an.
		/// </summary>
		public int Y { get { return this.pXY.Y; } }
	}
}
